/**
 * -------------------------------------------------------------
 * 增强文件上传, 可直接上传至本地服务器
 * 如果不支持h5, 则通过form提交到本地服务器(需要服务器支持)
 * -------------------------------------------------------------
 * <form>
 * <img data-zupload="true" src="file://xxx.png"/>
 * <input type="file" data-zupload="true" name="pictureFile" /> <!-- 上传文件 -->
 * </form>
 * -------------------------------------------------------------
 * 如果需要指定参数, 在form上配置data-zupload参数
 * <form data-zupload="{folder:'files',extra:{'module':'portrait'}}">
 * -------------------------------------------------------------
 * 配置data-zupload="true", 就会成为需要上传的文件, 只支持img和input type="file"
 * 如果配置data-zupload="xxx", 上传成功后的会根据xxx查找input来保存上传后的url; 如果找不到, 将会自动生成input[name=xxx]
 * -------------------------------------------------------------
 * $.fn.zupload(url, done);
 * $.fn.zupload(options);
 * -------------------------------------------------------------
 * @param options = {
 *		process:function(e), 进度回调函数, e = { element:DOMElement, index:int, count:int, percent:int }
 *		step:function(e), 每个文件上传成功的回调函数, e = { element:DOMElement, index:int, count:int, url:string }
 *		done:function(e), 全部完成后的回调函数; 如果没有需要上传的内容,或不支持直接上传, 则直接回调; e = { finish:int, count:int }
 *		error:function(e), 失败回调函数; return true则失败后仍然继续上传; e = { element:DOMElement, index:int, count:int, message:string }
 * }
 * count = 一共有几个文件需要上传
 * index = 当前上传第几个文件, 从0开始
 * finish = 完成了几个文件
 * percent = 当前文件上传进度, 0~100
 * step e.url = 文件上传成功后生成的url
 * error e.message = 失败原因
 * -------------------------------------------------------------
 * 注意: 
 * 1. img跨域问题
 * <img data-zupload="true" src="http://..." />, 会存在跨域的问题
 * 需要服务器允许且在img上加上crossOrigin才行, <img crossOrigin="*" src="..."/>
 * -------------------------------------------------------------
 * @author 赵卉华
 * date: 2017-02-20
 * -------------------------------------------------------------
 */
+function($) {
	var KEY = "__file_upload__";
	var INPUT = "data-zupload";
	$.fn.zupload = function(options, done) {
		if ($.dt.isString(options)) { options = { url:options }; }
		if (done && !options.done) { options.done = done; }

		return this.each(function() {
			var self = this;
			var $form = $(this);
			var o = $.extend(true, {}, $.fn.zupload.defaults, $form.zoptions("zupload"), options);
			var $files = o.find.call(self, o, INPUT);
			// 没有文件, 或者 不支持FormData(h5特性), 直接提交
			if ($files.length == 0 || !window.FormData) {
				$.isFunction(o.done) && o.done.call(self, { finish:0, count:$files.length });
				return;
			}

			upload.call(self, $files, o);
		});
	};
	$.fn.zupload.defaults = {
		url: "/m/actions/webfile/upload.json", // 上传文件的URL: Utils.getBaseUrl("actions/webfile/upload.json")
		// folder: "files", // files|temps
		errorCode: { },
		process: undefined,
		step: undefined,
		done: undefined,
		error: undefined,
		find : function(o, key) {
			return $(this).find("input[type=file],img").filter(function() {
				var input = $(this).attr(key);
				var matches = input != undefined && input != "false";
				if (matches && this.type == "file") {
					matches = this.files && this.files.length > 0;
				}
				return matches;
			});
		}
	};
	var upload = function($files, o) {
		var form = this;
		var $form = $(this);
		// 上传文件
		$files.each(function(i) {
			var file = this;
			var $file = $(this);
			var e = { element:file, index:i, count:$files.length };
			// 未选中文件
			if (this.files && this.files.length == 0) {
				/*
				// 未选择文件则跳过
				$form.queue(KEY, function() {
					handleError.call(form, o, $.extend({}, e, {result:{ code:'FileIsEmpty' }}));
				});
				*/
				return true;
			}
			// 上传回调函数
			var callback = function(percent, result) { // result = string|json{code,message}
				if (percent < 0) { // 上传失败
					handleError.call(form, o, $.extend({}, e, {result:result}));
					return;
				}

				if (percent < 100) {
					// 汇报进度
					$.isFunction(o.process) && o.process.call(form, $.extend({}, e, {percent:percent}));
				} else if (result) { // 上传完成
					// 分包上传时, 有可能多次返回100%, 但只有最后一次有result
					$.isFunction(o.process) && o.process.call(form, $.extend({}, e, {percent:100}));
					// 将文件URL填充到input中
					var $input = findOrCreateInput($form, $file);
					$input.val(result.url);
					$.isFunction(o.step) && o.step.call(form, $.extend({}, e, {url:result.url}));
					// 继续上传
					$form.dequeue(KEY);
				}
			};
			$form.queue(KEY, function() {
				var content;
				if ($file.is("img")) {
					// 将图片转换为blob, 加上type, 伪装成file对象
					content = imgToFile(file);
				} else {
					content = file.files[0]; 
				}
				var fd = new FormData();
				if (o.extra) {
					fd.append("extra", JSON.stringify(o.extra));
				}
				if (o.folder) {
					fd.append("folder", o.folder);
				}
				fd.append("filename", content.name);
				fd.append("file", content);
				$.ajax({ 
					url : o.url, 
					type : "POST",
					data : fd,
					dataType : "json",
					processData : false, // 不要处理
					contentType : false, // 不要处理
					success : function(json) { 
						if (json.body) {
							callback(100, { url:json.body });
						} else {
							console.log(json && json.message || "upload error");
						}
					}, 
					error : function(xhr) { 
						console.log("upload error");
					} 
				});
			});
		});
		// 全部完成后回调
		$form.promise(KEY).done(function() {
			$.isFunction(o.done) && o.done.call(self, { finish:$files.length, count:$files.length });
		});
		// 开始执行
		$form.dequeue(KEY);
	};
	var handleError = function(o, e) {
		var dequeue = true;
		if ($.isFunction(o.error)) {
			// e.result = string|json{code,message}
			if ($.isPlainObject(e.result)) {
				e.message = o.errorCode[e.result.code] || e.result.message;
			} else {
				e.message = e.result;
			}
			// return true则失败后仍然继续上传
			dequeue = o.error.call(this, e);
		}
		if (dequeue === true) { $(this).dequeue(KEY); }
	};
	var findOrCreateInput = function($form, $file) {
		var input = $file.attr(INPUT);
		// data-zupload="true", 只需上传, 不需要保存到input中
		// data-zupload="false", 在一开始就过滤掉了, 不会走到这里
		if (input == "true") { return $([]); }
		var $input;
		var i = $form.find("["+INPUT+"='"+ input +"']").index($file[0]);
		if (/\[i\]/.test(input)) { // data-zupload="image[i]"
			input = input.replace(/\[i\]/g, "["+i+"]"); // 把i替换成具体的序号
			$input = $form.find("input[name='"+ input +"']");
		} else { // data-zupload="image"
			$input = $form.find("input[name='"+ input +"']:eq("+i+")");
		}
		if ($input.length == 0) {
			$input = $('<input type="hidden" name="'+ input +'">').insertAfter($file);
		}
		return $input;
	};
	var imgToFile = function(img) {
		var base64Data = /^data:([\-\w]*\/[\-\w]*)(;charset=[\-\w]*)?(;base64)?,/i;
		var data;
		var mimeType = "image/png";
		var fileName = "image.png";
		if (base64Data.exec(img.src)) {
			data = img.src;
		} else {
			fileName = img.src.replace(/[\?#].*/, "");
			var index = fileName.lastIndexOf("/");
			if (index >= 0) { fileName = fileName.substring(index + 1); }
			var m = /\.(\w+)$/.exec(fileName);
			if (m) { mimeType = "image/" + m[1].replace(/jpg/, "jpeg"); }
			// 将图片画到画布上, 再转换为DataURL
			var canvas = document.createElement('canvas');
			canvas.width = img.naturalWidth;
			canvas.height = img.naturalHeight;
			canvas.style.display = "none";
			document.body.appendChild(canvas);
			canvas.getContext('2d').drawImage(img, 0, 0);

			// 这里, 会存在跨域的问题
			// 需要服务器允许且在img上加上crossOrigin才行, <img crossOrigin="*" src="..."/>
			data = canvas.toDataURL();
		}

		// DataURL转换为Blob
		// data = "data:image/png;base64,..."
		var bytes = window.atob(data.replace(base64Data, ''));
		var array = new Uint8Array(bytes.length);
		for (var i = 0; i < bytes.length; i++ ) {
			array[i] = bytes.charCodeAt(i);
		}

		var matcher = base64Data.exec(data);
		if (matcher) { 
			mimeType = matcher[1]; // 如mimeType=image/png
			fileName = fileName.replace(/\.[\-\w]+$/, "") + mimeType.replace(/(image\/)/, ".");
		}
		// 将图片转换为blob, 加上type和name, 伪装成file对象
		var blob = new Blob([array], { type:mimeType });
		blob.name = fileName;
		return blob;
	};
}(jQuery);
